﻿#include "threadcache.h"
#include "centralcache.h"

void* ThreadCache::fetchFromCentralCache(size_t index, size_t size)
{
	// 慢开始反馈调节算法
	// 1、最开始不会一次向central cache一次批量要太多，因为要太多了可能用不完
	// 2、如果你不要这个size大小内存需求，那么batchNum就会不断增长，直到上限
	// 3、size越大，一次向central cache要的batchNum就越小
	// 4、size越小，一次向central cache要的batchNum就越大
	size_t batchNum = min(m_freeLists[index].maxSize(), SizeClass::NumMoveSize(size));
	if (m_freeLists[index].maxSize() == batchNum)
	{
		m_freeLists[index].maxSize() += 1;
	}

	// 1. 从central cache中获取内存
	void* start = nullptr;
	void* end = nullptr;
	size_t actualNum = CentralCache::getInstance()->fetchRangeObj(start, end, batchNum, size);
	assert(actualNum > 0);
	// 2. 判断central cache中获取的内存是否只有一个
	if (actualNum == 1)
	{
		// 如果central cache中获取的内存只有一个，则直接返回
		assert(start == end);
		return start;
	}
	else
	{
		// 如果central cache中获取的内存不止一个，则将多余的内存插到thread cache的freeList中
		m_freeLists[index].pushRange(NextObj(start), end, actualNum - 1);
		return start;
	}
}

void* ThreadCache::allocate(size_t size)
{
	// 1. 判断申请内存是否大于256KB
	assert(size <= MAX_BYTES);
	// 2. 计算对齐后的大小
	size_t alignSize = SizeClass::RoundUp(size);
	// 3. 计算映射的自由链表桶
	size_t index = SizeClass::Index(size);
	// 4. 判断自由链表是否为空
	if (!m_freeLists[index].empty())
	{
		// 5. 如果自由链表不为空，则从自由链表中取内存
		return m_freeLists[index].pop();
	}
	else
	{	// 6. 如果自由链表为空，则从central cache中获取内存
		return fetchFromCentralCache(index, alignSize);
	}
}

void ThreadCache::deallocate(void* ptr, size_t size)
{
	// 1. 判断ptr是否为空
	assert(ptr);
	// 2. 判断size是否大于256KB
	assert(size <= MAX_BYTES);
	// 3. 找对映射的自由链表桶，对象插入进入
	// NOTE：这里不用计算对齐后的大小，这里size是经过计算的，是经过对齐的size
	size_t index = SizeClass::Index(size);
	m_freeLists[index].push(ptr);
	// 4. 当链表长度大于一次批量申请的内存时就开始还一段list给central cache
	if (m_freeLists[index].size() >= m_freeLists[index].maxSize())
	{
		listTooLong(m_freeLists[index], size);
	}
}

void ThreadCache::listTooLong(FreeList& list, size_t size)
{
	void* start = nullptr;
	void* end = nullptr;
	list.popRange(start, end, list.maxSize());

	CentralCache::getInstance()->releaseListToSpans(start, size);
}